package cn.xiuxius.embedded.processor;

import cn.xiuxius.embedded.dependencyscan.FileType;
import cn.xiuxius.embedded.dependencyscan.KeilFile;
import cn.xiuxius.embedded.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.stream.Stream;


@Slf4j
public class RTX51TinyPreProcessor {


    private final List<KeilFile> keilFiles;
    private final boolean needBackup;
    private Path backupDir;
    private boolean processed;
    private final Callable<Boolean> revivification = this::doRevivification;

    public RTX51TinyPreProcessor(List<KeilFile> keilFiles, boolean needBackup) {
        this.keilFiles = keilFiles;
        this.needBackup = needBackup;
        //过滤掉非C文件
        keilFiles.removeIf(file -> !file.getType().equals(FileType.C));
    }

    public Callable<Boolean> doProcessWithRevivification() {
        return doProcess() ? revivification : null;
    }

    public boolean doProcessOnly() {
        return doProcess();
    }

    public boolean doProcess() {
        boolean lastSuccess = true;
        if (needBackup) {
            lastSuccess = doBackup();
        }
        if (!lastSuccess) {
            return false;
        }
        lastSuccess = doPreProcess();
        if (!lastSuccess) {
            return false;
        } else {
            processed = true;
            return true;
        }
    }

    private boolean doBackup() {
        String backupDirName = "temp_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        Path backupRoot = Paths.get("build_temp");
        backupDir = backupRoot.resolve(backupDirName).normalize();

        try {
            cleanupOldBackups(backupRoot);
            if (!Files.exists(backupDir)) {
                Files.createDirectories(backupDir);
            }
            for (KeilFile file : keilFiles) {
                Path originalPath = Paths.get(file.getAbsolutePath());
                Path backupPath = backupDir.resolve(originalPath.getFileName());
                Files.copy(originalPath, backupPath, StandardCopyOption.REPLACE_EXISTING);
            }
            return true;
        } catch (IOException e) {
            log.error("Failed to backup files {}", e.getMessage());
            return false;
        }
    }

    private boolean doPreProcess() {
        try {
            for (KeilFile file : keilFiles) {
                Path originalPath = Paths.get(file.getAbsolutePath());
                Charset charset = CharsetUtil.detectCharset(originalPath);
                String content = Files.readString(originalPath, charset);
                String updatedContent = preprocessContent(content);
                if (!content.equals(updatedContent)) {
                    Files.writeString(originalPath, updatedContent, charset);
                }
            }
            return true;
        } catch (Exception e) {
            log.error("Failed to preprocess files {}", e.getMessage());
            if (needBackup && backupDir != null) {
                log.info("Rolling back...");
                // Rollback
                for (KeilFile file : keilFiles) {
                    try {
                        Path backupPath = backupDir.resolve(Paths.get(file.getAbsolutePath()).getFileName());
                        if (Files.exists(backupPath)) {
                            Files.copy(backupPath, Paths.get(file.getPath()), StandardCopyOption.REPLACE_EXISTING);
                        }
                    } catch (IOException ex) {
                        log.error("Failed to rollback file: {} {}", file.getPath(), ex.getMessage());
                    }
                }
            }
        }
        return false;
    }


    private boolean doRevivification() {
        if (!processed) return false;
        try {
            for (KeilFile file : keilFiles) {
                Path path = Paths.get(file.getAbsolutePath());
                Charset charset = CharsetUtil.detectCharset(path);
                String content = Files.readString(path, charset);
                String updatedContent = revivifyContent(content);
                if (!content.equals(updatedContent)) {
                    Files.writeString(path, updatedContent, charset);
                }
            }
            return true;
        } catch (Exception e) {
            //TODO 应该也回滚
            log.error("Failed to revivify files {}", e.getMessage());
            return false;
        }
    }

    private void cleanupOldBackups(Path backupRoot) {
        try (Stream<Path> dirs = Files.list(backupRoot)) {
            List<Path> sorted = dirs
                    .filter(Files::isDirectory)
                    .sorted(Comparator.comparing(Path::getFileName).reversed()) // 最新在前
                    .toList();

            if (sorted.size() > 5) {
                List<Path> toDelete = sorted.subList(5, sorted.size());
                for (Path dir : toDelete) {
                    deleteDirectoryRecursively(dir);
                }
            }
        } catch (IOException e) {
            log.warn("Failed to clean up old backups: {}", e.getMessage());
        }
    }

    private void deleteDirectoryRecursively(Path dir) throws IOException {
        if (Files.notExists(dir)) return;
        try (Stream<Path> paths = Files.walk(dir)) {
            paths.sorted(Comparator.reverseOrder())
                    .forEach(path -> {
                        try {
                            Files.delete(path);
                        } catch (IOException e) {
                            System.err.println("⚠️ Failed to delete: " + path + " (" + e.getMessage() + ")");
                        }
                    });
        }
    }


    private String preprocessContent(String content) {
        return content.replaceAll("/\\*\\* (_task_ \\w+) \\*\\*/", "$1");
    }

    private String revivifyContent(String content) {
        return content.replaceAll("(?<!/\\*\\*)\\b(_task_ \\w+)\\b(?! \\*\\*/)", "/** $1 **/");
    }


}
